package com.lianzai.payment.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.lianzai.car.domain.CarParkInfoDO;
import com.lianzai.car.service.CarInfoService;
import com.lianzai.car.service.CarLogService;
import com.lianzai.common.config.WxConfig;
import com.lianzai.common.utils.*;
import com.lianzai.common.utils.jackson.Json;
import com.lianzai.common.utils.okhttp.LzOkHttpBuilder;
import com.lianzai.common.utils.okhttp.LzOkHttpClient;
import com.lianzai.mobile.domain.CarPriceRelaDO;
import com.lianzai.mobile.domain.PayOrderDto;
import com.lianzai.mobile.domain.UserAccountDO;
import com.lianzai.mobile.service.CarPriceRelaService;
import com.lianzai.mobile.service.WxNotifyService;
import com.lianzai.shop.dao.OrderDao;
import com.lianzai.shop.model.TicketOrder;
import com.lianzai.shop.model.TicketOrderDO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

import com.lianzai.payment.dao.OrderInfoDao;
import com.lianzai.payment.domain.OrderInfoDO;
import com.lianzai.payment.service.OrderInfoService;

@Slf4j
@Service
public class OrderInfoServiceImpl implements OrderInfoService {
	@Autowired
	private OrderInfoDao orderInfoDao;

	@Autowired
	private OrderDao orderDao;

	@Autowired
	private WxConfig wxConfig;

	@Autowired
	private LzOkHttpClient lzOkHttpClient;

	@Autowired
	private Json json;

	@Autowired
	private CarPriceRelaService carPriceRelaService;

	@Autowired
	private WxNotifyService notifyService;

	@Autowired
	private CarInfoService carInfoService;

	@Autowired
	private CarLogService carLogService;

	@Override
	public OrderInfoDO get(Long orderId){
		return orderInfoDao.get(orderId);
	}

	@Override
	public List<OrderInfoDO> list(Map<String, Object> map){
		return orderInfoDao.list(map);
	}

	@Override
	public int count(Map<String, Object> map){
		return orderInfoDao.count(map);
	}

	@Override
	public int save(OrderInfoDO orderInfo){
		return orderInfoDao.save(orderInfo);
	}

	@Override
	public int update(OrderInfoDO orderInfo){
		return orderInfoDao.update(orderInfo);
	}

	@Override
	public int remove(Long orderId){
		return orderInfoDao.remove(orderId);
	}

	@Override
	public int batchRemove(Long[] orderIds){
		return orderInfoDao.batchRemove(orderIds);
	}

	@Override
	public R provisionalOrder(PayOrderDto payOrderDto)throws Exception{
		if(payOrderDto == null){
			return R.error("参数错误");
		}

		if(StringUtils.isBlank(payOrderDto.getCarNo())){
			return R.error("车牌号错误");
		}

		if(StringUtils.isBlank(payOrderDto.getExitId())){
			return R.error("出口ID错误");
		}

		//根据出口ID来获取金额
		CarParkInfoDO parkInfo = carLogService.getParkInfoByCameraId(Long.valueOf(payOrderDto.getExitId()));
		//此处使用字符串生成BigDecimal对象,如果直接使用float或double类型转可能出现精度丢失
		BigDecimal amt = new BigDecimal(parkInfo.getFee() + "");

		OrderInfoDO infoDO = new OrderInfoDO();
		infoDO.setOrderCode(UUIDUtil.getUUID());
		infoDO.setOrderPrice(amt);
		infoDO.setOrderPrePrice(BigDecimal.valueOf(0));
		infoDO.setOrderOldPrice(amt);
		infoDO.setOrderStatus(0);
		infoDO.setCusRemark(payOrderDto.getCusRemark());
		infoDO.setPayType(1);
		infoDO.setNums(1);
		infoDO.setCreateTime(new Date());
		infoDO.setOrderType(2);
		infoDO.setCarNo(payOrderDto.getCarNo());
		infoDO.setExitId(payOrderDto.getExitId());
		infoDO.setOpenId(payOrderDto.getOpenId());

		int row = orderInfoDao.save(infoDO);
		if(row <= 0) return R.error();
		return wxPayHandler(infoDO);
	}


	@Override
	public R placeOrder(PayOrderDto payOrderDto)throws Exception{
		if(payOrderDto == null){
			return R.error("参数错误");
		}

		if(payOrderDto.getNums()==null || payOrderDto.getNums()<=0){
			return R.error("购买月份数参数错误");
		}

		if(payOrderDto.getCarId()==null || payOrderDto.getCarId()<=0){
			return R.error("车辆ID错误");
		}

		CarPriceRelaDO carPriceRela = carPriceRelaService.get(payOrderDto.getCarId());

		BigDecimal amt = carPriceRela.getAmt().multiply(BigDecimal.valueOf(payOrderDto.getNums()));
		OrderInfoDO infoDO = new OrderInfoDO();
		infoDO.setOrderCode(UUIDUtil.getUUID());
		infoDO.setOrderPrice(amt);
		infoDO.setOrderPrePrice(BigDecimal.valueOf(0));
		infoDO.setOrderOldPrice(amt);
		infoDO.setOrderStatus(0);
		infoDO.setUserId(payOrderDto.getUserId());
		infoDO.setCarId(payOrderDto.getCarId());
		infoDO.setCusRemark(payOrderDto.getCusRemark());
		infoDO.setPayType(1);
		infoDO.setNums(payOrderDto.getNums());
		infoDO.setCreateTime(new Date());
		infoDO.setOrderType(1);
		infoDO.setCarNo(carInfoService.get(payOrderDto.getCarId().intValue()).getCarNo());
		infoDO.setOpenId(payOrderDto.getOpenId());

		UserAccountDO userAccountDO = notifyService.selectUserEndDate(payOrderDto.getUserId(), payOrderDto.getCarId());
		if(userAccountDO==null || userAccountDO.getCarExpireDate()==null){
			Date currDate = new Date();
			infoDO.setStartTime(currDate);
			infoDO.setEndTime(DateUtils.getBeforeMonth(currDate, payOrderDto.getNums()));
		}else{
			Date currDate = userAccountDO.getCarExpireDate();
			infoDO.setStartTime(currDate);
			infoDO.setEndTime(DateUtils.getBeforeMonth(currDate, payOrderDto.getNums()));
		}

		int row = orderInfoDao.save(infoDO);
		if(row <= 0) return R.error();
		return wxPayHandler(infoDO);
	}


	private R wxPayHandler(OrderInfoDO infoDO)throws Exception{
		Map<String,String> params = new TreeMap<String,String>();
		params.put("appid", wxConfig.getAppid());
		params.put("mch_id", wxConfig.getMchid());
		params.put("trade_type", "JSAPI");
		params.put("nonce_str",UUID.randomUUID().toString().replaceAll("-", "").toLowerCase());
		params.put("body", infoDO.getRemark());
		params.put("out_trade_no", infoDO.getOrderCode());
		params.put("total_fee", String.valueOf(infoDO.getOrderPrice().setScale(2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")).longValue()));
		params.put("spbill_create_ip", IpUtil.getLocalHostAddress().getHostAddress());
		params.put("notify_url", wxConfig.getNotifyUrl());
		params.put("openid", infoDO.getOpenId());
		params.put("sign_type", "MD5");
		params.put("limit_pay", "no_credit");
		String signStr = ParamsUtils.map2StrByAscii(params).concat("&key=").concat(wxConfig.getPrivateKey());
		params.put("sign", MD5Util.MD5Encode(signStr).toUpperCase());
		String body = XmlUtils.toXml(params);
		log.info("请求微信统一下单接口请求参数: {}" ,body);
		BaseResult<String> result = lzOkHttpClient.postJson(
				LzOkHttpBuilder.builder()
						.url(wxConfig.getUnifiedorderUrl())
						.body(body)
						.typeReference(new TypeReference<String>() {}).build());
		log.info("请求微信统一下单接口响应参数: {}" , json.obj2string(result));

		Map<String, String> resultMap = XmlUtils.toMap(result.getData().getBytes("UTF-8"), "UTF-8");
		String resultSignStr = ParamsUtils.map2StrByAscii(
				new TreeMap<String,String>(resultMap))
				.concat("&key=")
				.concat(wxConfig.getPrivateKey());
		String sign = resultMap.get("sign");
		if(StringUtils.isBlank(sign) && !sign.equalsIgnoreCase(MD5Util.MD5Encode(resultSignStr))) {
			return R.error(4001, "微信响应签名验证错误");
		}

		String status = resultMap.get("return_code") ;
		String resultCode = resultMap.get("result_code") ;
		String message = resultMap.get("return_msg") ;
		Map<String, String> resultParams = new HashMap();
		if("SUCCESS".equals(status) && "SUCCESS".equals(resultCode)) {
			resultParams.put("appId", wxConfig.getAppid());
			resultParams.put("timeStamp",String.valueOf(System.currentTimeMillis() / 1000));
			resultParams.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32));
			resultParams.put("signType", "MD5");
			resultParams.put("package", "prepay_id=" + resultMap.get("prepay_id"));
			String resultParamsSign = ParamsUtils.map2StrByAscii(new TreeMap<String,String>(resultParams)).concat("&key=").concat(wxConfig.getPrivateKey());
			resultParams.put("sign", MD5Util.MD5Encode(resultParamsSign).toUpperCase());
		}else {
			return R.error(500, message);
		}
		return R.okObj(resultParams);
	}



	public R placeXiangparkOrder(PayOrderDto payOrderDto) throws Exception
	{
		if(payOrderDto == null){
			return R.error("参数错误");
		}

		if(payOrderDto.getNums()==null || payOrderDto.getNums()<=0){
			return R.error("购买月份数参数错误");
		}

		if(payOrderDto.getCarId()==null || payOrderDto.getCarId()<=0){
			return R.error("车辆ID错误");
		}

		CarPriceRelaDO carPriceRela = carPriceRelaService.get(payOrderDto.getCarId());

		BigDecimal amt = carPriceRela.getAmt().multiply(BigDecimal.valueOf(payOrderDto.getNums()));
		OrderInfoDO infoDO = new OrderInfoDO();
		infoDO.setOrderCode(UUIDUtil.getUUID());
		infoDO.setOrderPrice(amt);
		infoDO.setOrderPrePrice(BigDecimal.valueOf(0));
		infoDO.setOrderOldPrice(amt);
		infoDO.setOrderStatus(0);
		infoDO.setUserId(payOrderDto.getUserId());
		infoDO.setCarId(payOrderDto.getCarId());
		infoDO.setCusRemark(payOrderDto.getCusRemark());
		infoDO.setPayType(1);
		infoDO.setNums(payOrderDto.getNums());
		infoDO.setCreateTime(new Date());
		infoDO.setOrderType(1);
		infoDO.setCarNo(carInfoService.get(payOrderDto.getCarId().intValue()).getCarNo());
		infoDO.setOpenId(payOrderDto.getOpenId());

		UserAccountDO userAccountDO = notifyService.selectUserEndDate(payOrderDto.getUserId(), payOrderDto.getCarId());
		if(userAccountDO==null || userAccountDO.getCarExpireDate()==null){
			Date currDate = new Date();
			infoDO.setStartTime(currDate);
			infoDO.setEndTime(DateUtils.getBeforeMonth(currDate, payOrderDto.getNums()));
		}else{
			Date currDate = userAccountDO.getCarExpireDate();
			infoDO.setStartTime(currDate);
			infoDO.setEndTime(DateUtils.getBeforeMonth(currDate, payOrderDto.getNums()));
		}

		int row = orderInfoDao.save(infoDO);
		if(row <= 0) return R.error();
		return xiangparkPayHandler(infoDO);

	}


	public R placeXiangparkTicketOrder(PayOrderDto payOrderDto) throws Exception
	{
		if(payOrderDto == null){
			return R.error("参数错误");
		}

		if(payOrderDto.getNums()==null || payOrderDto.getNums()<=0){
			return R.error("购买停车券数量参数错误");
		}

		//CarPriceRelaDO carPriceRela = carPriceRelaService.get(payOrderDto.getCarId());

		//BigDecimal amt = carPriceRela.getAmt().multiply(BigDecimal.valueOf(payOrderDto.getNums()));


		TicketOrderDO infoDO = new TicketOrderDO();
		infoDO.setOrderCode(UUIDUtil.getUUID());
		infoDO.setOrderPrice(payOrderDto.getTransAmt());
		infoDO.setPayAmt(payOrderDto.getTransAmt());
		//infoDO.setOrderPrePrice(BigDecimal.valueOf(0));
		//infoDO.setOrderOldPrice(payOrderDto.getTransAmt());
		infoDO.setOrderStatus(0);
		infoDO.setUserId(payOrderDto.getUserId());
		infoDO.setRemark("购买停车券");
		infoDO.setPayType(1);
		infoDO.setNums(payOrderDto.getNums());
		infoDO.setCreateTime(new Date());
		infoDO.setOpenId(payOrderDto.getOpenId());
		infoDO.setOrderTicketNum(payOrderDto.getNums().longValue());
		infoDO.setTicketId(payOrderDto.getProductId());


		int row = orderDao.add(infoDO);
		if(row <= 0) return R.error();
		return xiangparkPayHandler(infoDO);

	}


	private R xiangparkPayHandler(OrderInfoDO infoDO)throws Exception{
		Map<String,String> params = new TreeMap<String,String>();

		params.put("appid", wxConfig.getXiangparkAppId());
		//params.put("appid", wxConfig.getAppid());

		params.put("mch_id", wxConfig.getMchid());
		//params.put("mch_id", wxConfig.getJingjinzhinengMchId());


		params.put("trade_type", "JSAPI");
		params.put("nonce_str",UUID.randomUUID().toString().replaceAll("-", "").toLowerCase());
		params.put("body", infoDO.getRemark());
		params.put("out_trade_no", infoDO.getOrderCode());
		params.put("total_fee", String.valueOf(infoDO.getOrderPrice().setScale(2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal("100")).longValue()));
		params.put("spbill_create_ip", IpUtil.getLocalHostAddress().getHostAddress());
		params.put("notify_url", wxConfig.getNotifyUrl());
		params.put("openid", infoDO.getOpenId());
		params.put("sign_type", "MD5");
		String signStr = ParamsUtils.map2StrByAscii(params).concat("&key=").concat(wxConfig.getPrivateKey());
		params.put("sign", MD5Util.MD5Encode(signStr).toUpperCase());
		String body = XmlUtils.toXml(params);
		log.info("请求微信统一下单接口请求参数: {}" ,body);
		BaseResult<String> result = lzOkHttpClient.postJson(
				LzOkHttpBuilder.builder()
						.url(wxConfig.getUnifiedorderUrl())
						.body(body)
						.typeReference(new TypeReference<String>() {}).build());
		log.info("请求微信统一下单接口响应参数: {}" , json.obj2string(result));

		Map<String, String> resultMap = XmlUtils.toMap(result.getData().getBytes("UTF-8"), "UTF-8");
		String resultSignStr = ParamsUtils.map2StrByAscii(
				new TreeMap<String,String>(resultMap))
				.concat("&key=")
				.concat(wxConfig.getPrivateKey());
		String sign = resultMap.get("sign");
		if(StringUtils.isBlank(sign) || !sign.equalsIgnoreCase(MD5Util.MD5Encode(resultSignStr))) {
			return R.error(4001, "微信响应签名验证错误");
		}

		String status = resultMap.get("return_code") ;
		String resultCode = resultMap.get("result_code") ;
		String message = resultMap.get("return_msg") ;
		Map<String, String> resultParams = new HashMap();
		if("SUCCESS".equals(status) && "SUCCESS".equals(resultCode)) {
			resultParams.put("appId", wxConfig.getXiangparkAppId());
			resultParams.put("timeStamp",String.valueOf(System.currentTimeMillis() / 1000));
			resultParams.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32));
			resultParams.put("signType", "MD5");
			resultParams.put("package", "prepay_id=" + resultMap.get("prepay_id"));
			String resultParamsSign = ParamsUtils.map2StrByAscii(new TreeMap<String,String>(resultParams)).concat("&key=").concat(wxConfig.getPrivateKey());
			resultParams.put("sign", MD5Util.MD5Encode(resultParamsSign).toUpperCase());
		}else {
			return R.error(500, message);
		}
		return R.okObj(resultParams);
	}
}
